package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"runtime"
	"smart-flow/pusher/proto"
	"smart-flow/pusher/sys"
	"smart-flow/pusher/transform"
	"strconv"
	"strings"
	"syscall"
	"time"
)

const (
	PusherVer = 520
)

var (
	pusherId  string
	infoUrl   string
	duration  int64
	rateLimit int64
	address   string
	pidFile   string // PID文件路径
	quit      chan os.Signal
)

func init() {
	flag.StringVar(&pusherId, "i", "", "Pusher唯一标识")
	flag.StringVar(&infoUrl, "m", "", "管理地址")
	flag.Int64Var(&duration, "d", 0, "执行时间(单位 分钟)")
	flag.Int64Var(&rateLimit, "r", 0, "限速速率(Kbps)")
	flag.StringVar(&address, "a", "", "目标地址")
	pidFile = filepath.Join(os.TempDir(), "sfa.pid")
}

func main() {
	flag.Parse()               // 无论主进程还是子进程，都需要调用 flag.Parse() 来解析命令行参数
	nonFlagArgs := flag.Args() // 返回的是非标志参数（non-flag arguments）我们约定最后一个非标志参数为 "child" 时，表示这是一个子进程
	if len(nonFlagArgs) > 0 && nonFlagArgs[len(nonFlagArgs)-1] == "child" {
		childProcess()
	} else {
		mainProcess()
	}
}

func mainProcess() {
	handleArguments() // 调用函数显示并处理这些参数
	if isChildProcessRunning() {
		fmt.Println("child-running")
		return
	}
	startBackgroundChildProcess()
}

func handleArguments() {
	if strings.TrimSpace(pusherId) == "" {
		fmt.Println("Hello Android!")
		os.Exit(2)
	}
	if strings.TrimSpace(infoUrl) == "" {
		fmt.Println("Hello Android!")
		os.Exit(2)
	}
	if duration < 1 || duration > 1440 {
		fmt.Println("Hello Android!")
		os.Exit(2)
	}
	if rateLimit < 100 {
		fmt.Println("Hello Android!")
		os.Exit(2)
	}
	if strings.TrimSpace(address) == "" {
		fmt.Println("Hello Android!")
		os.Exit(2)
	}
}

func startBackgroundChildProcess() {
	args := []string{
		"-i", pusherId,
		"-m", infoUrl,
		"-d", strconv.FormatInt(duration, 10),
		"-r", strconv.FormatInt(rateLimit, 10),
		"-a", address,
		"child",
	}
	cmd, err := sys.Daemonize(args)
	if err != nil {
		panic("Failed to start background process: " + err.Error())
	}
	err = cmd.Process.Release()
	if err != nil {
		panic("Failed to release child process: " + err.Error())
	}
}

func childProcess() {
	fmt.Printf("child-started-%d\n", os.Getpid())
	_ = createPIDFile()
	defer removePIDFile()
	setupSignalHandlers() // 设置信号处理，确保在收到终止信号时也能删除PID文件
	infoResp := handleInfo(proto.PusherInfo{
		PusherId:          pusherId,
		PusherVer:         PusherVer,
		PusherTime:        time.Now().Unix(),
		PusherRateLimit:   rateLimit,
		PusherV6Reachable: 0,
		PusherDuration:    duration,
	})
	if infoResp == nil {
		shutdown()
	}
	if infoResp.BeRunning == 0 {
		shutdown()
	}
	speed := transform.SpeedTest(infoResp.SpeedUrl, infoResp.SpeedDuration)
	loginResp := handleLogin(proto.PusherInfo{
		PusherId:          pusherId,
		PusherVer:         PusherVer,
		PusherTime:        time.Now().Unix(),
		PusherRateLimit:   rateLimit,
		PusherV6Reachable: 0,
		PusherDuration:    duration,
	}, proto.PusherBandwidthInfo{
		Bandwidth: speed,
	}, infoResp.LoginUrl)
	if loginResp == nil {
		shutdown()
	}
	sender := new(transform.DataSender).Init(loginResp, 2*transform.MB, address, pusherId, shutdown)
	if sender == nil {
		shutdown()
	}
	sender.Start()
	serverTime := loginResp.Timestamp
	endTime := loginResp.Timestamp + duration*60
	interval := time.Duration(loginResp.PusherInterval) * time.Second
	for {
		time.Sleep(interval)
		aliveResp := handleAlive(proto.PusherInfo{
			PusherId:          pusherId,
			PusherVer:         PusherVer,
			PusherTime:        time.Now().Unix(),
			PusherRateLimit:   rateLimit,
			PusherV6Reachable: 0,
			PusherDuration:    duration,
		}, proto.PusherBandwidthInfo{
			Bandwidth: speed,
		}, proto.PusherRunTimeInfo{
			SrvLastTime: serverTime,
			EndTime:     endTime,
			DebugInfo:   "",
		}, infoResp.AliveUrl)
		if aliveResp == nil {
			sender.Stop()
			shutdown()
		}
		serverTime = aliveResp.Timestamp
		if serverTime >= endTime {
			sender.Stop()
			shutdown()
		}
	}
}

func handleRequest(body []byte, url string) []byte {
	resp, err := http.Post(url, "application/json", bytes.NewBuffer(body))
	if err != nil || resp == nil {
		return nil
	}
	defer func(resp *http.Response) {
		if resp != nil && resp.Body != nil {
			_ = resp.Body.Close()
		}
	}(resp)
	if resp.StatusCode != http.StatusOK {
		return nil
	}
	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil
	}
	// fmt.Printf("获取服务器信息成功, 数据:%s\n", string(respBody))
	return respBody
}

func handleInfo(pusherInfo proto.PusherInfo) *proto.PusherInfoResp {
	reqBody := proto.PusherInfoReq{
		PusherInfo: pusherInfo,
	}
	body, _ := json.Marshal(reqBody)
	respBody := handleRequest(body, infoUrl)
	if respBody == nil {
		return nil
	}
	data := new(proto.PusherInfoResp)
	err := json.Unmarshal(respBody, data)
	if err != nil {
		return nil
	}
	return data
}

func handleLogin(pusherInfo proto.PusherInfo, bandwidthInfo proto.PusherBandwidthInfo, url string) *proto.PusherLoginResp {
	reqBody := proto.PusherLoginReq{
		PusherInfo:          pusherInfo,
		PusherBandwidthInfo: bandwidthInfo,
	}
	body, _ := json.Marshal(reqBody)
	respBody := handleRequest(body, url)
	if respBody == nil {
		return nil
	}
	data := new(proto.PusherLoginResp)
	err := json.Unmarshal(respBody, data)
	if err != nil {
		return nil
	}
	return data
}

func handleAlive(pusherInfo proto.PusherInfo, bandwidthInfo proto.PusherBandwidthInfo, runtimeInfo proto.PusherRunTimeInfo, url string) *proto.PusherAliveResp {
	reqBody := proto.PusherAliveReq{
		PusherInfo:          pusherInfo,
		PusherBandwidthInfo: bandwidthInfo,
		PusherRunTimeInfo:   runtimeInfo,
	}
	body, _ := json.Marshal(reqBody)
	respBody := handleRequest(body, url)
	if respBody == nil {
		return nil
	}
	data := new(proto.PusherAliveResp)
	err := json.Unmarshal(respBody, data)
	if err != nil {
		return nil
	}
	return data
}

func isChildProcessRunning() bool {
	pidBytes, err := os.ReadFile(pidFile) // 读取PID文件
	if err != nil {
		if os.IsNotExist(err) {
			return false
		}
		// fmt.Printf("读取PID文件失败: %v\n", err)
		return false
	}
	pidStr := strings.TrimSpace(string(pidBytes))
	pid, err := strconv.Atoi(pidStr)
	if err != nil {
		// fmt.Printf("解析PID失败: %v\n", err)
		removePIDFile()
		return false
	}
	process, err := os.FindProcess(pid)
	if err != nil {
		removePIDFile()
		return false
	}
	if runtime.GOOS != "windows" {
		err = process.Signal(syscall.Signal(0)) // 发送信号0来检查进程是否真的存在
		if err != nil {
			removePIDFile()
			return false
		}
	}
	return true
}

func createPIDFile() error {
	pid := os.Getpid()
	pidStr := strconv.Itoa(pid)
	return os.WriteFile(pidFile, []byte(pidStr), 0644)
}

func removePIDFile() {
	_ = os.Remove(pidFile)
}

func setupSignalHandlers() {
	quit = make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		<-quit
		// fmt.Println("收到终止信号，Pusher清理资源并关闭...")
		removePIDFile()
		os.Exit(0)
	}()
}

func shutdown() {
	quit <- syscall.SIGTERM
	time.Sleep(5 * time.Second)
	os.Exit(0)
}
